/**
 * Copyright (C) 2010 David Simonek <dafesimonek@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.dafe.partyband.dochandler;

import com.sun.star.beans.PropertyValue;
import com.sun.star.comp.helper.BootstrapException;
import com.sun.star.frame.TerminationVetoException;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XDesktop;
import com.sun.star.frame.XStorable;
import com.sun.star.frame.XTerminateListener;
import com.sun.star.io.IOException;
import com.sun.star.lang.DisposedException;
import com.sun.star.lang.EventObject;
import com.sun.star.lang.IllegalArgumentException;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.uno.Exception;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.CloseVetoException;
import com.sun.star.util.XCloseable;
import com.sun.star.view.PrintJobEvent;
import com.sun.star.view.PrintableState;
import com.sun.star.view.XPrintJobBroadcaster;
import com.sun.star.view.XPrintJobListener;
import com.sun.star.view.XPrintable;

import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;

/**
 *
 * @author dafe
 */
public class OOActions {

    private static final Object DESKTOP_LOCK = new Object();
    private static volatile Object desktop;
    private static volatile boolean bootstrapFailed = false;
    private static volatile Future<?> desktopLoadFuture;
    private static RequestProcessor RP = new RequestProcessor(OOActions.class);
    private static final ExecutorService EXEC = Executors.newSingleThreadExecutor();

    public static boolean isOpenSupported () {
        return getDesktop(false) != null;
    }

    public static boolean isPrintSupported () {
        return isOpenSupported();
    }

    public static void print (final File content) {
        RP.post(new Runnable() {
            volatile PrintableState state = null;

            @Override
            public void run() {
                XComponent doc = loadDocument(content, true);
                XPrintable printable = (XPrintable) UnoRuntime.queryInterface(XPrintable.class, doc);
                XPrintJobBroadcaster selection = (XPrintJobBroadcaster)
                            UnoRuntime.queryInterface(XPrintJobBroadcaster.class,
                            printable);
                selection.addPrintJobListener(new XPrintJobListener() {

                    @Override
                    public void printJobEvent(PrintJobEvent pje) {
                        System.out.println("print job state: " + pje.toString());
                        state = pje.State;
                    }

                    @Override
                    public void disposing(EventObject eo) {
                        System.out.println("disposing...");
                    }
                });

                try {
                    printable.print(new PropertyValue[0]);
                } catch (IllegalArgumentException ex) {
                    Exceptions.printStackTrace(ex);
                }

                // wait until it's safe to close
                while (true) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                    if (state != null) {
                        break;
                    }
                }
                
                XCloseable xcloseable = (XCloseable) UnoRuntime.queryInterface(XCloseable.class, doc);
                try {
                    xcloseable.close(false);
                } catch (CloseVetoException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        });
    }

    public static void open (File file) {
        loadDocument(file, false);
    }

    public enum DocumentType {
        WORD("doc", "MS Word 97"), PDF("pdf", "writer_pdf_Export");

        private String extension, conversionFilter;

        private DocumentType (String extension, String conversionFilter) {
            this.extension = extension;
            this.conversionFilter = conversionFilter;
        }

        public String getConversionFilter() {
            return conversionFilter;
        }

        public String getExtension() {
            return extension;
        }
    }

    public static void convert (File content, DocumentType docType) {
        XComponent doc = loadDocument(content, true);
	XStorable xStorable = (XStorable)UnoRuntime.queryInterface(XStorable.class, doc);

	// Set properties for conversions
	PropertyValue[] conversionProperties = new PropertyValue[2];

	conversionProperties[0] = new PropertyValue();
	conversionProperties[0].Name = "Overwrite";
	conversionProperties[0].Value = Boolean.TRUE;
        
	conversionProperties[1] = new PropertyValue();
	conversionProperties[1].Name = "FilterName";
	conversionProperties[1].Value = docType.conversionFilter;

        String docUrl = getFileURL(content);
       // Appending the favoured extension to the origin document name
        int index = docUrl.lastIndexOf('.');
        String storeUrl = docUrl.substring(0, index + 1) + docType.getExtension();

	try {
            // Convert
            xStorable.storeToURL(storeUrl, conversionProperties);

            // Closing the converted document. Use XCloseable.clsoe if the
            // interface is supported, otherwise use XComponent.dispose
            XCloseable xCloseable = (XCloseable)UnoRuntime.queryInterface(XCloseable.class, xStorable);

            if ( xCloseable != null) {
                xCloseable.close(false);
            } else {
                XComponent xComp = (XComponent)UnoRuntime.queryInterface(XComponent.class, xStorable);
                xComp.dispose();
            }
	} catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        } catch (CloseVetoException ex) {
            Exceptions.printStackTrace(ex);
        }

    }

    private static XComponentLoader getCompLoader() {
        XDesktop xDesktop = null;
        try {
            xDesktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class, getDesktop(true));
        } catch (DisposedException exc) {
            // workaround, OOffice fails to notify about termination sometimes, don't know why
            clearContext();
            xDesktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class, getDesktop(true));
        }
        xDesktop.addTerminateListener(new XTerminateListener() {
            @Override
            public void queryTermination(EventObject eo) throws TerminationVetoException {
            }

            @Override
            public void notifyTermination(EventObject eo) {
                System.out.println("TERMINATING DESKTOP!!!!!!!");
                clearContext();
            }

            @Override
            public void disposing(EventObject eo) {
            }
        });
        return (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, getDesktop(true));
    }

    private static Object getDesktop (boolean wait) {
        synchronized (DESKTOP_LOCK) {
            if (desktop == null && !bootstrapFailed && desktopLoadFuture == null) {
                desktopLoadFuture = EXEC.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            XComponentContext xContext = OOBootstrap.bootstrap();
                            XMultiComponentFactory xMCF = xContext.getServiceManager();
                            desktop = xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);
                        } catch (BootstrapException ex) {
                            Exceptions.printStackTrace(ex);
                            bootstrapFailed = true;
                        } catch (Exception ex) {
                            bootstrapFailed = true;
                            Exceptions.printStackTrace(ex);
                        }
                    }
                });
                if (wait) {
                    try {
                        desktopLoadFuture.get();
                    } catch (InterruptedException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (ExecutionException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }
            }
        }
        return desktop;
    }

    private static void clearContext () {
        synchronized (DESKTOP_LOCK) {
            desktop = null;
            desktopLoadFuture = null;
        }
    }

    private static String getFileURL (File file) {
        StringBuilder sbTmp = new StringBuilder("file:///");
        try {
            sbTmp.append(file.getCanonicalPath().replace('\\', '/'));
        } catch (java.io.IOException ex) {
            Exceptions.printStackTrace(ex);
        }
        return sbTmp.toString();
    }

    private static XComponent loadDocument (File docFile, boolean hidden) {
        XComponentLoader locXCompLoader = getCompLoader();
        XComponent result = null;
        PropertyValue[] loadProperties = new PropertyValue[1];
	loadProperties[0] = new PropertyValue();
	loadProperties[0].Name = "Hidden";
	loadProperties[0].Value = Boolean.valueOf(hidden);

        try {
            // Load a Writer document, which will be optionally displayed
            result = locXCompLoader.loadComponentFromURL(getFileURL(docFile), "_blank", 0, loadProperties);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IllegalArgumentException ex) {
            Exceptions.printStackTrace(ex);
        }
        return result;
    }

}
